package org.jeecg;

import com.alibaba.druid.sql.visitor.functions.If;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;

/**
 * xml转为实体类（包括赋值）
 *
 * @author liujiamiao
 * @email 2544296058@qq.com
 * @date 2020-10-26 21:23
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BasicXmlGenerate {

    //存放类的属性
    private static Map<String,Object> map = new HashMap<>();
    //类名
    private static String clazzName = "";
    //包名
    private static String packageName = null;
    //文件读取路径以及生成路径
    private static String path = null;
    //注释
    private static List<String> psList = new ArrayList<>();

    //@XmlAttribute("type-ggg")

    public static void main(String[] args) throws IOException {
        //文件路径
        String jsonName = "GenerateXml.xml";
        //包名
        packageName = "com.chunuo.generate";
        //路径
        path = "D:\\JAVA\\poi\\";

        //读取文件内容
        String xml = read(path, jsonName);

        //构建java文本
        String java = build(xml);
        System.out.println(java);

        //写入到java类中
        write(path+(packageName.replace('.','\\')),clazzName+".java",java.toString());

    }

    /**
     * 初始化，重要代码
      * @param xml
     * @return
     */
    public static String build(String xml){
        //java类的内容，拼完串就写到文件里面
        StringBuilder java = new StringBuilder();
        java.append("package "+packageName+";\r\n\r\nimport java.util.*;\r\n");

        //遍历完类标签，将其存到list中
        LinkedList<Xml> list = getClassTag(xml);

        //把类标签（第一个标签和最后一个标签）取出来
        Xml firstXmlLeft = list.pollFirst();
        Xml firstXmlRight = list.pollFirst();
        Xml lastXmlLeft = list.pollLast();
        Xml lastXmlRight = list.pollLast();

        //添加注释
        java.append("/**\r\n");
        if(psList!=null&&psList.size()>0){
            for(String ps:psList){
                java.append(" * "+ps+"\r\n");
            }
        }
        java.append(
                " * @author liujiamiao\n" +
                " * @email 2544296058@qq.com\r\n" +
                " */\r\npublic class ");

        //得到类名
        clazzName = xml.substring(firstXmlLeft.getIndex()+1, firstXmlRight.getIndex()).trim();
        String clazzEnd = xml.substring(lastXmlLeft.getIndex()+2, lastXmlRight.getIndex()).trim();
//        System.out.println(clazzName+":"+clazzEnd);
        if(clazzName.indexOf(clazzEnd)==-1){
            throw new RuntimeException("标签格式不对，没有找到对应的类标签");
        }
        java.append(clazzName+"{\r\n");

        //获取所有标签的下标 <
        list = getTagFieldIndex(xml,firstXmlRight.getIndex()+1,lastXmlLeft.getIndex());
        if(list.size()%4!=0){
            throw new RuntimeException("标签缺失，请检查");
        }
        LinkedList<Field> fieldList = getTagField(xml,list);
        //将属性添加到java中
        while(!fieldList.isEmpty()){
            Field field = fieldList.remove();
            String fieldName = field.getName();
            String type = field.getType();
            Object value = field.getValue();
            if(type.equals("String")){
                java.append("\tprivate "+field.type+" "+field.name+" = \""+field.value+(field.type.equalsIgnoreCase("Float")?"F":"")+"\";\r\n");
            }else{
                java.append("\tprivate "+field.type+" "+field.name+" = "+field.value+(field.type.equalsIgnoreCase("Float")?"F":"")+";\r\n");
            }
        }

        //写构造方法
        java.append("\tpublic "+clazzName+"() {\r\n");
        Set<Map.Entry<String, Object>> entrySet = map.entrySet();
        for(Map.Entry<String,Object> entry:entrySet){
            String key = entry.getKey();
            String[] field = key.split(" ");
            Object value = entry.getValue();
            if(value instanceof List){
                System.out.println(value);
                //list集合
                List ls = (List) value;
                for(int i = 0;i<ls.size();i++){
                    if(field[1].equalsIgnoreCase("String")||(field[1].equalsIgnoreCase("Object")&&isString(ls.get(i)))){
                        java.append("\t\t"+field[0]+".add(\""+ls.get(i)+"\");\r\n");
                    }else{
                        java.append("\t\t"+field[0]+".add("+ls.get(i)+");\r\n");
                    }
                }
            }else if(value instanceof Map){
                System.out.println(value);
                //map集合
                Map<String,Object> m = (HashMap) value;
                Set<Map.Entry<String, Object>> entries = m.entrySet();
                for(Map.Entry e:entries){
                    if(field[1].equalsIgnoreCase("String")||(field[1].equalsIgnoreCase("Object")&&isString(e.getValue()))) {
                        java.append("\t\t" + field[0] + ".put(\"" + e.getKey() + "\",\"" + e.getValue() + "\");\r\n");
                    }else{
                        java.append("\t\t" + field[0] + ".put(\"" + e.getKey() + "\"," + e.getValue() + ");\r\n");
                    }
                }
            }
        }

        //结尾
        java.append(
                "\t}\r\n" +
                "}");
        return java.toString();
    }

    /**
     * 是否是字符串
     * @param val
     * @return
     */
    public static boolean isString(Object val){
        try {
            int i = Integer.parseInt(val.toString());
            return false;
        } catch (NumberFormatException e) {
            return true;
        }
    }

    /**
     * 获取xml 的类标签
     * @param xml
     * @return
     */
    public static LinkedList getClassTag(String xml){
        //直接过滤掉xml的<?xml version="1.0" encoding="UTF-8"?>
        int index = xml.indexOf("?>")+2;
        LinkedList<Xml> list = new LinkedList<>();
        //优先读取类标签
        for(int i = index;i<xml.length();i++){
            //添加注释
            if(xml.charAt(i)=='#'){
                int j = i +1;
                while(xml.charAt(j)!='\r'){
                    j++;
                }
                psList.add(xml.substring(i+1,j));
                i = j + 2;
                System.out.println(psList);
            }
            //找到第一个<，即是类标签
            if(xml.charAt(i)=='<'){
                list.add(new Xml(0,i,false));
            }
            //第一个>即是类的结束标签
            if(xml.charAt(i)=='>'){
                list.add(new Xml(1,i,false));
                break;
            }
        }
        for(int i = xml.length()-1;i>index;i--){
            //找到第一个</，即是类标签
            if(xml.charAt(i)=='<'&&xml.charAt(i+1)=='/'){
                list.add(new Xml(0,i,true));
                break;
            }
            //第一个>即是类的结束标签
            if(xml.charAt(i)=='>'){
                list.add(new Xml(1,i,true));
            }
        }
        return list;
    }


    /**
     * 获取标签的下标 <  >  </  >
     * @param xml
     * @return
     */
    public static LinkedList getTagFieldIndex(String xml,int left,int right){
        //标志该标签是否到结尾了，如</name>
        boolean end = false;
        //将标签的下标全部记录到里面
        int num = 0;
        LinkedList<Xml> list = new LinkedList<Xml>();
        for(int i = left;i<right;i++){
            //找到<同时，避免是不完整的xml格式，确保还有下一个字符
            if(xml.charAt(i)=='<'&&i+1<xml.length()){
                if(xml.charAt(i+1)=='/'){
                    //</可以确定是结束标签了
                    end = true;
                    if(num==1)
                        list.add(new Xml(0,i,true));
                    num--;
                }else{
                    //非结束标签
                    end = false;
                    if(num==0)
                        list.add(new Xml(0,i,false));
                    num++;
                }
            }
            if(xml.charAt(i)=='>'){
                //如果上一个<是结束标签，那么这个一定也是结束标签
                if(end==true){
                    end = false;
                    if(num==0)
                        list.add(new Xml(1,i,true));
                }else{
                    if(num==1)
                        list.add(new Xml(1,i,false));

                }
            }
        }
        return list;
    }

    /**
     * 将标签转为Field对象，封装标签的信息
     * @param xml
     * @param list
     * @return
     */
    public static LinkedList<Field> getTagField(String xml,LinkedList<Xml> list){
        LinkedList<Field> fieldList = new LinkedList<>();
        while(!list.isEmpty()){
            //截取出<xxxxx>的xxxx
            Xml xmlLeft = list.pollFirst();
            Xml xmlRight = list.pollFirst();
            String str = trim(xml.substring(xmlLeft.getIndex()+1, xmlRight.getIndex()).trim());
            // str可能是这样子的  =》》name type = " String "
            //空格左边绝对是name，如果没有找到空格，则可能是<name> 没有设置任何的类型，默认object
            int indexName = str.indexOf(' ');
            //字段名
            String fieldName = null;
            if(indexName!=-1){
                //属性名
                fieldName = str.substring(0,indexName);
            }else{

                if(str.indexOf('"')==-1){
                    //没有“”说明没有type属性
                    fieldName = str;
                }else if((indexName = str.indexOf("type"))!=-1){
                    //有“”又有type，说明标签忘记加空格了，自动给他修正
                    fieldName = str.substring(0,indexName);
                }
            }

            //找到"xxxx"左右两边的引号
            int indexTypeLeft = str.indexOf("\"");
            int indexTypeRight = str.lastIndexOf("\"");

            //属性类型默认为object
            String fieldType = "Object";
            //确保能找到""
            if(indexTypeLeft!=-1&&indexTypeRight!=-1&&indexTypeLeft<indexTypeRight){
                //属性类型
                fieldType = str.substring(indexTypeLeft+1,indexTypeRight).trim();
            }

            //把结束标签的>也取下来
            Xml xmlTempLeft = list.pollFirst();
            Xml xmlTempRight = list.pollFirst();
            //属性值
            String fieldValue = xml.substring(xmlRight.getIndex()+1,xmlTempLeft.getIndex()).trim();
            int isCollection = 0;
            if(fieldValue.indexOf("<list>")!=-1){
                //这里是集合的处理方式
                isCollection = 1;
                int left = -1;
                int right = -1;
                List ls = new ArrayList<>();
                while((left = fieldValue.indexOf("<list>"))!=-1&&(right = fieldValue.indexOf("</list>"))!=-1){
                    ls.add(fieldValue.substring(left+6,right).trim());
                    fieldValue = fieldValue.substring(right+7).trim();
                }
                map.put(fieldName+" "+fieldType,ls);
                fieldValue = "new ArrayList<"+fieldType+">()";
                fieldType = "List<"+fieldType+">";
            }else if(fieldValue.indexOf("<entry>")!=-1&&fieldValue.indexOf("<key>")!=-1&&fieldValue.indexOf("<value>")!=-1){
               //这里是map类型的处理方式
                isCollection = 2;
                int left = -1;
                int right = -1;
                Map m = new HashMap();
                while((left = fieldValue.indexOf("<entry>"))!=-1&&(right = fieldValue.indexOf("</entry>"))!=-1){
                    String temp = fieldValue.substring(left + 7, right);
                    int i = temp.indexOf("<key>");
                    int j = temp.indexOf("</key>");
                    int i1 = temp.indexOf("<value>");
                    int j2 = temp.indexOf("</value>");
                    if(i!=-1&&j!=-1&&i<j&&i1!=-1&&j2!=-1&&i1<j2){
//                        if("String".equalsIgnoreCase(fieldType)){
//                            sb.append("\""+temp.substring(i+5,j)+"\":\""+temp.substring(i1+7,j2)+"\"");
//                        }else{
//                            sb.append("\""+temp.substring(i+5,j)+"\":"+temp.substring(i1+7,j2)+"");
//                        }
                        m.put(temp.substring(i+5,j).trim(),temp.substring(i1+7,j2).trim());
                    }
                    fieldValue = fieldValue.substring(right+6).trim();
                }
                map.put(fieldName+" "+fieldType,m);
                fieldValue = "new HashMap<String,"+fieldType+">()";
                fieldType = "Map<String,"+fieldType+">";
            }
            fieldList.add(new Field(fieldName,fieldType,fieldValue,isCollection));

        }
        return fieldList;
    }

    /**
     * 读取文件内容
     * @param path
     * @param fileName
     * @return
     * @throws IOException
     */
    public static String read(String path ,String fileName) throws IOException {
        //读取
        FileReader reader = new FileReader(new File(path,fileName));
        char[] buf = new char[50];
        int len = -1;
        StringBuilder sb = new StringBuilder();
        while((len = reader.read(buf))!=-1){
            sb.append(new String(buf,0,len));
        }
        reader.close();
        return sb.toString();
    }

    /**
     * 写入文件
     * @param path
     * @param fileName
     * @param content
     * @throws IOException
     */
    public static void write(String path,String fileName,String content) throws IOException {
        File filePath = new File(path);
        if(!filePath.exists()){
            filePath.mkdirs();
        }
        File file = new File(path, fileName);
        FileWriter writer = new FileWriter(file);
        writer.write(content);
        writer.flush();
        writer.close();
    }

    //去掉多余的空格
    public static String trim(String str){
        char[] ch = str.toCharArray();
        StringBuilder sb = new StringBuilder();
        //前一个字符是否是空格
        boolean flag = false;
        for(int i = 0;i<ch.length;i++){
            if(ch[i]!=' '){
                sb.append(ch[i]);
                flag = false;
            }else{
                //前一个不是空格
                if(!flag){
                    flag = true;
                    sb.append(ch[i]);
                }
            }
        }
        return sb.toString();
    }

    public static void show(List list){
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }

}
@AllArgsConstructor
@NoArgsConstructor
@Data
class Xml{
    @ApiModelProperty("0表示（<） 1表示（>）")
    int flag;

    @ApiModelProperty("下标")
    int index;

    @ApiModelProperty("是否是</标签")
    boolean end;

    public boolean getEnd() {
        return this.end;
    }
}

@AllArgsConstructor
@NoArgsConstructor
@Data
class Field{
    @ApiModelProperty("属性名称")
    String name;

    @ApiModelProperty("属性的类型")
    String type;

    @ApiModelProperty("属性值")
    Object value;

    @ApiModelProperty("0-不是结合，1-list类型集合，2-map类型集合")
    int isCollection;
}